package com.jaws.core.common.util;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.SecureRandom;

import lombok.extern.slf4j.Slf4j;

import com.google.code.yanf4j.util.ConcurrentHashSet;
import com.jaws.core.common.enums.SnowflakeModeEnum;
import com.jaws.core.common.exception.JawsCommonExceptionCode;
import com.jaws.core.common.log.Log;
import com.jaws.core.configuration.CommonProperties;
import com.taobao.stresstester.StressTestUtils;
import com.taobao.stresstester.core.StressResult;
import com.taobao.stresstester.core.StressTask;

/**
 * 雪花算法生成id
 * |-------------时间戳31位-------------| |--------16位机器-----| |------17位并发------|
 * 初始时间戳为2010-11-04(原作者设定，经讨论不修改)
 * 0000000000 0000000000 0000000000 0 0000000000 000000 0000000000 0000000<br>
 * 
 * @author lixin
 * @date 2018-03-29 10:21:18
 * @since v1.0.0
 */
@Slf4j
public class SnowflakeIdK8sUtil {

	/**
	 * 基准时间
	 * Thu, 04 Nov 2010 01:42:54 GMT
	 */
	private static long twepoch = 1514736000L;

	// /**
	// * 区域标志位数
	// */
	// private final static long regionIdBits = 3L;

	/**
	 * 机器标识位数
	 */
	private final static long workerIdBits = 16L;

	/**
	 * 序列号识位数
	 */
	private final static long sequenceBits = 17L;

	/**
	 * 机器ID最大值
	 */
	private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);

	/**
	 * 序列号ID最大值
	 */
	private final static long sequenceMask = -1L ^ (-1L << sequenceBits);

	/**
	 * 机器ID偏左移10位
	 */
	private final static long workerIdShift = sequenceBits;

	/**
	 * 时间毫秒左移33位
	 */
	private final static long timestampLeftShift = sequenceBits + workerIdBits;

	private static long lastTimestamp = -1L;

	private static long sequence = 0L;

	/**
	 * 第几个机器，0-65535，最多可以扩到第65536个机器
	 */
	private static long workerId = 0L;

	/**
	 * 默认不启用雪花算法
	 */
	private static boolean isUsed = false;

	public static void init(CommonProperties commonProperties) {
		log.info(Log.op("SnowflakeIdUtil.init").kv("commonProperties.getSnowflakeMode()", commonProperties.getSnowflakeMode()).toString());
		try {
			switch (commonProperties.getSnowflakeMode()) {
			case HOST:
				workerId = generateWorkerIdByHost();
				break;
			case IPV4:
				workerId = generateWorkerIdByIpv4();
				break;
			case CONFIG:
				workerId = commonProperties.getSnowflakeWorkerId();
				break;
			default:
				workerId = generateWorkerIdByHost();
				break;
			}
		} catch (Exception e) {
			throw JawsCommonExceptionCode.SNOWFLAKE_CENTERID_ERROR.exp(e);
		}

		if (workerId > maxWorkerId) {
			throw JawsCommonExceptionCode.SNOWFLAKE_CENTERID_ERROR.exp();
		}

		isUsed = true;
		log.info(Log.op("SnowflakeIdUtil.init").kv("isUsed", isUsed).toString());
	}

	public static long generate() {
		if (!isUsed) {
			throw JawsCommonExceptionCode.SNOWFLAKE_SWITH_OFF.exp();
		}
		return nextId();
	}

	/**
	 * 实际产生代码的
	 *
	 * @param isPadding
	 * @param busId
	 * @return
	 */
	private static synchronized long nextId() {

		long timestamp = timeGen();

		if (timestamp < lastTimestamp) {

			// 如果还没好则抛异常(后续加报警代码实现)
			if (timestamp < lastTimestamp) {
				try {
					throw new Exception("Clock moved backwards.  Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
				} catch (Exception e) {
					e.printStackTrace();
				}
			} else {
				return generate();
			}

		}

		// 如果上次生成时间和当前时间相同,在同一毫秒内
		if (lastTimestamp == timestamp) {
			// sequence自增，因为sequence只有10bit，所以和sequenceMask相与一下，去掉高位
			sequence = (sequence + 1) & sequenceMask;
			// 判断是否溢出,也就是每毫秒内超过1024，当为1024时，与sequenceMask相与，sequence就等于0
			if (sequence == 0) {
				// 自旋等待到下一毫秒
				System.out.println(1111111);
				timestamp = tailNextMillis(lastTimestamp);
			}
		} else {
			// 如果和上次生成时间不同,重置sequence，就是下一毫秒开始，sequence计数重新从0开始累加,
			// 为了保证尾数随机性更大一些,最后一位设置一个随机数
			sequence = new SecureRandom().nextInt(10);
		}

		lastTimestamp = timestamp;

		return ((timestamp - twepoch) << timestampLeftShift) | (workerId << workerIdShift) | sequence;
	}

	// 防止产生的时间比之前的时间还要小（由于NTP回拨等问题）,保持增量的趋势.
	private static long tailNextMillis(final long lastTimestamp) {
		long timestamp = timeGen();
		while (timestamp <= lastTimestamp) {
			timestamp = timeGen();
		}
		return timestamp;
	}

	// 获取当前的时间戳
	protected static long timeGen() {
		return System.currentTimeMillis() / 1000L;
	}

	/**
	 * ip后两个网段计算，要求前面网段一致
	 *
	 * @return
	 * @author maojian
	 * @date 2018-03-16 10:37:52
	 * @since v1.0.0
	 */
	private static long generateWorkerIdByIpv4() {
		Long workerId;

		InetAddress address;

		try {
//			 address = InetAddress.getByName(addressStr);
			address = InetAddress.getLocalHost();
		} catch (final UnknownHostException e) {
			throw JawsCommonExceptionCode.SNOWFLAKE_INETADDRESS_ERROR.exp();
		}
		byte[] ipAddressByteArray = address.getAddress();

		workerId = (long) (((ipAddressByteArray[ipAddressByteArray.length - 2] & 0xFF) << Byte.SIZE) + (ipAddressByteArray[ipAddressByteArray.length - 1] & 0xFF));

		return workerId;
	}

	/**
	 * 根据hostname，取“-”后的数字
	 *
	 * @return
	 * @author maojian
	 * @date 2018-03-16 10:40:09
	 * @since v1.0.0
	 */
	private static long generateWorkerIdByHost() {
		InetAddress address;
		Long workerId;
		try {
			address = InetAddress.getLocalHost();
		} catch (final UnknownHostException e) {
			throw new IllegalStateException("Cannot get LocalHost InetAddress, please check your network!");
		}
		String hostName = address.getHostName();
		log.info(Log.op("SnowflakeIdUtil.generateWorkerIdByHost").kv("hostName", hostName).toString());

		try {
			String lastHostName = hostName.substring(hostName.lastIndexOf("-") + 1);
			log.info(Log.op("SnowflakeIdUtil.generateWorkerIdByHost").kv("lastHostName", lastHostName).toString());

			workerId = Long.valueOf(lastHostName);
			log.info(Log.op("SnowflakeIdUtil.generateWorkerIdByHost").kv("workerId", workerId).toString());
		} catch (final NumberFormatException e) {
			throw new IllegalArgumentException(String.format("Wrong hostname:%s, hostname must be end with number!", hostName));
		}

		return workerId;
	}

	public static void main(String[] args) throws UnknownHostException{
		CommonProperties c = new CommonProperties();
		ConcurrentHashSet<Long> s = new ConcurrentHashSet();

		c.setSnowflakeMode(SnowflakeModeEnum.IPV4);
		SnowflakeIdK8sUtil.init(c);
	    log.info(Log.op("test").msg("begin").toString());
		StressResult stressResult = StressTestUtils.test(500, 270_000, new StressTask() {
			public Object doTask() throws Exception {
				long id = SnowflakeIdK8sUtil.generate();
				s.add(id);
				// System.out.println(id);
				return null;
			}
		}, 0);
	    log.info(Log.op("test").msg("" + s.size()).toString());
	    log.info(Log.op("test").msg(StressTestUtils.format(stressResult).toString()).toString());
//
//		System.out.println(s.size());
//		long time = System.currentTimeMillis();
		// System.out.println(new Date(twepoch));
		// String dateStr = "2018-01-01 00:00:00 000";
		// DateTime date = DateTime.parse(dateStr, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss SSS"));
		// System.out.println("基准时间戳:" + date.getMillis());
		//
		//
		// long beginTime = 1514736000000L;
		//
		// long beginTime2 = 1514736000L;
		//
		//
		// // String dateStr = "2010-11-04 09:42:54 500";
		// String dateStr1 = "2018-06-01 12:00:00 000";
		// DateTime date1 = DateTime.parse(dateStr1, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss SSS"));
		// System.out.println((date1.getMillis() - twepoch) << 23);
		//
		// String dateStr2 = "2021-09-03 12:00:00 000";
		// DateTime date2 = DateTime.parse(dateStr2, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss SSS"));
		// System.out.println((date2.getMillis()/1000L - beginTime2) << 33);
		//
		// String dateStr3 = "2021-09-03 12:00:02 000";
		// DateTime date3 = DateTime.parse(dateStr3, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss SSS"));
		// System.out.println((date3.getMillis()/1000L - beginTime2) << 33);
		//
		//
		// // System.out.println(new DateTime(new Date(time)).toString("yyyy-MM-dd HH:mm:ss"));
		// System.out.println(Math.pow(2, 30)/(365*24*60*60L));
		// System.out.println(Math.pow(2, 40)/(365*24*60*60*1000L));
		//
		// System.out.println(Math.pow(2, 16)/(365*10));
		// System.out.println(Math.pow(2, 16));
		//
		// System.out.println(Math.pow(2, 17));

//		Set<String> sets = Sets.newHashSet();
//		int num = 0;
//		for (int i = 0; i < 256; i++) {
//			for (int j = 0; j < 255; j++) {
//				num ++;
////				String workerId =  String.valueOf((((ipAddressByteArray[ipAddressByteArray.length - 2] & 0xFF) << Byte.SIZE) + (ipAddressByteArray[ipAddressByteArray.length - 1] & 0xFF)));
//				String workerId = String.valueOf(generateWorkerIdByIpv4("255.255." + i + "." + j));
//				sets.add(workerId);
//				System.out.println(workerId);
//			}
//		}
//		
//		System.out.println("================= size =====" + sets.size());
//		System.out.println("================= num =====" + num);


	}

}
